`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:01:46 09/27/2011 
// Design Name: 
// Module Name:    CPU_FSM
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module CPU_Pipelined(CLK, reset, ballx, bally, p1, p2, p1_input, p2_input, p1_score, p2_score);
`include "opcode.v"

input CLK;
input reset;

output [9:0] ballx;
output [8:0] bally;
output [8:0] p1, p2; //paddle
output [15:0] p1_score, p2_score; //paddle
input [11:0] p1_input;
input [11:0] p2_input;

wire LCD_E, LCD_RS, LCD_RW;
wire [11:8] SF_D;
wire stall;

//Forwarding
wire [1:0] ForwardA,ForwardB;

//Connections
wire [4:0] PSR,F;
wire [3:0] lowerop, EX_WRITE_ADDRESS, MEM_WRITE_ADDRESS,WB_WRITE_ADDRESS, EX_RD_ADDR1, EX_RD_ADDR2;
wire [7:0] EX_OP, EX_IMM_DATA;
wire [9:0] PC, PC_in, PC_inc, PC_imm;
wire [15:0] RAM_DO,IR,ID_RD_DATA1,EX_RD_DATA1, ID_RD_DATA2,EX_RD_DATA2, EX_RESULT, MEM_RESULT, WB_RESULT, WB_DM_DO, B_in, WB_WRITE_DATA, MEM_DM_DO, EX_RD_DATA1_FORWARD, EX_RD_DATA2_FORWARD, EX_IR;

//control wires
wire ID_DM_WE,EX_DM_WE,ID_MEMTOREG,EX_MEMTOREG,ID_IMM_EN,EX_IMM_EN,ID_WR_EN,EX_WR_EN,EX_PC_Jump,ID_MEMREAD,EX_MEMREAD, MEM_MEMREAD, WB_PC_Jump, jumpstall, jumpimmediate;

reg [2:0] state;

//Pipeline Registers

//IF/ID Registers
register16_flush IR_reg(RAM_DO,IR,~jumpstall,(reset|EX_PC_Jump),CLK,EX_PC_Jump);

//ID/EX Registers
register16 ID_EX_IR(IR,EX_IR,1'b1,reset,CLK);
register16 ID_EX_RD_DATA1(ID_RD_DATA1,EX_RD_DATA1,1'b1,reset,CLK);
register16 ID_EX_RD_DATA2(ID_RD_DATA2,EX_RD_DATA2,1'b1,reset,CLK);
register8  ID_EX_OP({IR[15:12],IR[7:4]},EX_OP,1'b1,reset,CLK);
register8  ID_EX_IMM_DATA(IR[7:0],EX_IMM_DATA,1'b1,reset,CLK);
register4  ID_EX_WRITE_ADDRESS(IR[11:8],EX_WRITE_ADDRESS,1'b1,reset,CLK);
register4  ID_EX_RD_ADDR1(IR[11:8],EX_RD_ADDR1,1'b1,reset,CLK);
register4  ID_EX_RD_ADDR2(IR[3:0],EX_RD_ADDR2,1'b1,reset,CLK);

//EX/MEM Registers
register16 EX_MEM_RESULT(EX_RESULT,MEM_RESULT,1'b1,reset,CLK);
register4  EX_MEM_WRITE_ADDRESS(EX_WRITE_ADDRESS,MEM_WRITE_ADDRESS,1'b1,reset,CLK);

//MEM/WB Registers
register16 MEM_WB_DM_DO(MEM_DM_DO,WB_DM_DO,1'b1,reset,CLK);
register16 MEM_WB_RESULT(MEM_RESULT,WB_RESULT,1'b1,reset,CLK);
register4  MEM_WB_WRITE_ADDRESS(MEM_WRITE_ADDRESS,WB_WRITE_ADDRESS,1'b1,reset,CLK);


//ID/EX Control registers
register1 ID_EX_DM_WE(ID_DM_WE,EX_DM_WE,1'b1,reset,CLK);
register1 ID_EX_MEMTOREG(ID_MEMTOREG,EX_MEMTOREG,1'b1,reset,CLK);
register1 ID_EX_IMM_EN(ID_IMM_EN,EX_IMM_EN,1'b1,reset,CLK);
register1 ID_EX_WR_EN(ID_WR_EN,EX_WR_EN,1'b1,reset,CLK);
register1 ID_EX_MEMREAD(ID_MEMREAD,EX_MEMREAD,1'b1,reset,CLK);

//EX/MEM control registers
register1 EX_MEM_DM_WE(EX_DM_WE,MEM_DM_WE,1'b1,reset,CLK);
register1 EX_MEM_PC_Jump(EX_PC_Jump,MEM_PC_Jump,1'b1,reset,CLK);
register1 EX_MEM_MEMTOREG(EX_MEMTOREG,MEM_MEMTOREG,1'b1,reset,CLK);
register1 EX_MEM_WR_EN(EX_WR_EN,MEM_WR_EN,1'b1,reset,CLK);
register1 EX_MEM_MEMREAD(EX_MEMREAD, MEM_MEMREAD, 1'b1,reset,CLK);

//MEM/WB control registers
register1 MEM_WB_MEMTOREG(MEM_MEMTOREG,WB_MEMTOREG,1'b1,reset,CLK);
register1 MEM_WB_WR_EN(MEM_WR_EN,WB_WR_EN,1'b1,reset,CLK);
register1 MEM_WB_PC_Jump(MEM_PC_Jump,WB_PC_Jump,1'b1,reset,CLK);

//Control assignments
assign ID_DM_WE=(({IR[15:12],IR[7:4]} == STOR)&(~stall)) ? 1:0;
assign ID_MEMTOREG=({IR[15:12],IR[7:4]} == LOAD) ? 1:0;
assign ID_MEMREAD =({IR[15:12],IR[7:4]} == LOAD) ? 1:0;

assign ID_WR_EN=(({IR[15:12]== 0 & IR[7:4]}>0)|(IR[15:14]==2'b10)|(IR[13:12]!=2'b00))&(IR[15:12]!=4'b1011)&(IR[7:4]!=4'b1011)&(~stall);

wire JumpOP, JEQ, JNE, JLE, JL, JGE, JGT, JUC;

assign JumpOP=({EX_IR[15:12],EX_IR[7:4]} == 8'h4c);
assign JEQ=((EX_IR[11:8] == 4'b0000)& PSR[4]);
assign JNE=((EX_IR[11:8] == 4'b0001)&~PSR[4]);
assign JLE=((EX_IR[11:8] == 4'b0111)&~PSR[1]);
assign JLT=((EX_IR[11:8] == 4'b1100)&(~PSR[4] & ~PSR[1]));
assign JGE=((EX_IR[11:8] == 4'b1101)&(PSR[4]|PSR[1]));
assign JGT=((EX_IR[11:8] == 4'b0110)&PSR[1]);
assign JUC=(EX_IR[11:8] == 4'b1110);

assign jumpimmediate = ((EX_IR[15:12] == 4'h8)&(EX_IR[7:6] == 2'b10)) | ((EX_IR[15:12] == 4'h8)&(EX_IR[7:6] == 2'b11)&PSR[4]);

assign EX_PC_Jump = (JumpOP&(JEQ|JNE|JLE|JLT|JGE|JGT|JUC))|jumpimmediate;

assign ID_IMM_EN=(IR[15:12] != 4'h0 && IR[15:12] != 4'h4 && IR[15:12] != 4'hc) ? 1:0;

//Hazards
assign stall = (EX_MEMREAD&((EX_WRITE_ADDRESS==ID_RD_DATA1)|(EX_WRITE_ADDRESS==ID_RD_DATA2)));
assign jumpstall = EX_PC_Jump|MEM_PC_Jump;

//EX Forwarding
assign ForwardA[0] = WB_WR_EN & (~ForwardA[1]) & (WB_WRITE_ADDRESS == EX_RD_ADDR1);
assign ForwardA[1] = MEM_WR_EN & (MEM_WRITE_ADDRESS == EX_RD_ADDR1);
//MEM Forwarding
assign ForwardB[0] = WB_WR_EN & (~ForwardB[1]) & (WB_WRITE_ADDRESS == EX_RD_ADDR2);
assign ForwardB[1] = MEM_WR_EN & (MEM_WRITE_ADDRESS == EX_RD_ADDR2);

//Forwarding Muxes
mux31_16bit ForwardA_mux(EX_RD_DATA1,WB_RESULT,MEM_RESULT, EX_RD_DATA1_FORWARD,ForwardA);
mux31_16bit ForwardB_mux(EX_RD_DATA2,WB_RESULT,MEM_RESULT, EX_RD_DATA2_FORWARD,ForwardB);

//Datapath Muxes
mux21_16bit PC_jimux(PC_imm,{EX_IR[11:8], EX_IR[5:0]},PC_in,(EX_PC_Jump&jumpimmediate));
mux21_16bit PC_mux(PC_inc,EX_RESULT[9:0],PC_imm,EX_PC_Jump);


mux21_4bit immediate_op_mux(EX_OP[3:0],4'h0,lowerop,EX_IMM_EN);
mux21_16bit Bimmediate_mux(EX_RD_DATA2_FORWARD,{{8{1'b0}},{EX_IMM_DATA}},B_in,EX_IMM_EN);
mux21_16bit MEMTOREG_mux(WB_RESULT,WB_DM_DO,WB_WRITE_DATA,WB_MEMTOREG);

//Program Counter
Counter PC_module(CLK, reset, PC_in, PC, PC_inc, ~stall);

//Instruction RAM and Data RAM
//module RAM(DI,DO,ADDR,CLK,RESET,WE,EN);
//RAM IM(16'h0000,RAM_DO,PC,CLK,reset,1'b0,1'b1);
//module RAM_IM(clka,wea,addra,dina,douta)
RAM_IM IM(CLK, 1'b0, PC, 16'h0000, RAM_DO);

RAM DM(MEM_RESULT,MEM_DM_DO,MEM_RESULT,CLK,reset,MEM_DM_WE,1'b1);

//PSR	
register5 PSR_reg(F,PSR,1'b1,reset,CLK);

//Writedata in and out are created so that an imediate value can be passed in instead of value b from the register.
Regfile registers(CLK, reset, IR[11:8], IR[3:0], ID_RD_DATA1, ID_RD_DATA2, WB_WR_EN, WB_WRITE_ADDRESS, WB_WRITE_DATA, ballx, bally, p1, p2, p1_input, p2_input, p1_score, p2_score);
//ALUUnit(A, B, op, result, F, Cin);
ALUUnit alu(.A(EX_RD_DATA1_FORWARD), .B(B_in), .op({EX_OP[7:4],lowerop}), .result(EX_RESULT), .F(F), .Cin(1'b0));

//S3Etest S2eTest(CLK, reset, reg2_data, SF_CE0, SF_D, LCD_E, LCD_RS, LCD_RW);
lcd_ctrl LCD(CLK, reset, EX_RD_DATA1, SF_D, LCD_E, LCD_RS, LCD_RW);

always@(posedge CLK) begin
	 
	if (reset) begin
		state = boot;
	end else begin
		
		case(state)
		boot: state = fetch;
		fetch: state = decode;
		decode: state = execute;
		execute: begin
			
			state = mem;
			
		end
		mem: state = wb;
		wb: state = fetch;
		endcase
	
	end
	
end

endmodule
